home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Tools / Languages / MacMarlais 0.5.3 / DIRM Examples / Random.dyl < prev   
Encoding:
Text File  |  1994-09-19  |  17.0 KB  |  542 lines  |  [TEXT/Mrls]

  1. module:        dylan-user
  2. author:        dpierce@cs.cmu.edu
  3. synopsis:        This file implements random numbers for the Gwydion
  4.             implementation of Dylan. (Hacked by beard@cs.ucdavis for Marlais.)
  5. copyright:    Copyright (C) 1994, Carnegie Mellon University.
  6.             All rights reserved.
  7. rcs-header:    $Header: distributions.dylan,v 1.2 94/06/28 23:57:55 wlott Exp $
  8.  
  9. //======================================================================
  10. //
  11. // Copyright (c) 1994  Carnegie Mellon University
  12. // All rights reserved.
  13. // 
  14. // Use and copying of this software and preparation of derivative
  15. // works based on this software are permitted, including commercial
  16. // use, provided that the following conditions are observed:
  17. // 
  18. // 1. This copyright notice must be retained in full on any copies
  19. //    and on appropriate parts of any derivative works.
  20. // 2. Documentation (paper or online) accompanying any system that
  21. //    incorporates this software, or any part of it, must acknowledge
  22. //    the contribution of the Gwydion Project at Carnegie Mellon
  23. //    University.
  24. // 
  25. // This software is made available "as is".  Neither the authors nor
  26. // Carnegie Mellon University make any warranty about the software,
  27. // its performance, or its conformity to any specification.
  28. // 
  29. // Bug reports, questions, comments, and suggestions should be sent by
  30. // E-mail to the Internet address "gwydion-bugs@cs.cmu.edu".
  31. //
  32. //======================================================================
  33.  
  34. /* Random Number Distributions
  35.  
  36.    This file contains class definitions and functions for using random
  37.    number distributions.  The basis of these methods is the generation
  38.    of a uniform distribution on the interval [0, 1) using the Linear
  39.    Congruential Method.
  40.  
  41.    Other probability distributions are generated using transformations
  42.    of the basic unit uniform distribution.  Some that are defined here
  43.    are other uniform distribution, normal, and exponential
  44.    distributions.
  45.  
  46.    This implementation defines an abstract class
  47.    <random-distribution>.  All subclasses of <random-distribution>
  48.    must define a method for the generic function RANDOM.  This
  49.    function returns a random number in the distribution.
  50.  
  51. */
  52.  
  53. define module Random
  54.    use dylan;
  55.    export
  56.       <random-distribution>, random,
  57.  
  58.       <uniform-distribution>,
  59.       <unit-uniform-distribution>,
  60.       <real-uniform-distribution>,
  61.       <integer-uniform-distribution>,
  62.       <exponential-distribution>,
  63.       <normal-distribution>,
  64.  
  65.       *dylan-random-seed*, *dylan-random-distribution*,
  66.       random-uniform, seed-random!,
  67.  
  68.       chi-square;
  69. end module Random;
  70.  
  71. // <random-distribution> -- public
  72. //
  73. // Abstract superclass of random distributions.  Each subclass must
  74. // define a method for RANDOM, which returns a random number from the
  75. // distribution.
  76. //
  77. define abstract class <random-distribution> (<object>) end class;
  78.  
  79.  
  80. // random -- public
  81. //
  82. // Methods on this function return a random number generated from
  83. // whichever distribution is being used.
  84. //
  85. define generic random (random-distribution :: <random-distribution>)
  86.    => random :: <real>;
  87.  
  88.  
  89. /* Unit Uniform Distribution
  90.  
  91.               Linear Congruential Method
  92.  
  93.    The basic algorithm used to generate random numbers is called the
  94.    Linear Congruential Method (see, for example, Sedgewick,
  95.    Algorithms, chapter 35).  The Linear Congruential method uses a
  96.    recurrence
  97.  
  98.                z  = a z    + 1 (mod m)
  99.                         k      k-1
  100.  
  101.    to generate pseudo-random sequences of integers uniformly
  102.    distributed between 0 and m - 1.  The first z is called the seed of
  103.    the sequence.
  104.  
  105.    There are some basic rules for the values of the constants (see
  106.    Sedgewick).  The value of m should be large.  Since Mindy has 30
  107.    bit integers, we take m to be
  108.  
  109.                                      28
  110.                 m = 2
  111.  
  112.                                   = 268 435 456
  113.  
  114.    The value of a should not be as large, perhaps one digit shorter
  115.    than m, and it should end with the three digits x21 where x is
  116.    even.  So we take a (arbitrarily) to be
  117.  
  118.                     a = 29 413 621
  119.  
  120.    Now, we cannot calculate each z directly because the calculation
  121.    would overflow the integer size.  Instead we multiply (a z) in
  122.    parts.  We take k to be the square root of m.  Then if
  123.  
  124.            p = p1 k + p0 and q = q1 k + q0
  125.  
  126.    then
  127.  
  128.          p q = (p1 q1) m + (p1 q0 + p0 q1) k + p0 q0
  129.  
  130.    and
  131.  
  132.       p q (mod m) = ((p1 q0 + p0 q1 (mod k)) k + p0 q0) (mod m)
  133.  
  134.    Using this method we can calculate (a z) (mod m) without overflow.
  135.  
  136.    Now a uniform distribution on [0, 1) can be found by dividing the
  137.    random variable z by m.
  138.  
  139. */
  140.  
  141. // <uniform-distribution> -- public
  142. // 
  143. // Abstract superclass of uniform distributions.  Uniform
  144. // distributions are distributions such that every element of the
  145. // range should appear with equal frequency.
  146. //
  147. define abstract class <uniform-distribution> (<random-distribution>) end class;
  148.  
  149. // <unit-uniform-distribution> -- public
  150. //
  151. // This concrete class provides the implementation of the basic
  152. // uniform random number distribution on [0, 1).  It uses the linear
  153. // congruential method.
  154. //
  155. define class <unit-uniform-distribution> (<uniform-distribution>)
  156.    slot random-seed :: <integer>,
  157.       init-function: method () *dylan-random-seed* end method,
  158.       init-keyword: seed:;
  159. end class;
  160.  
  161.  
  162. // random -- public
  163. // 
  164. // Returns a random number from the unit uniform distribution.  Uses
  165. // the linear congruential method described above.  An integer between
  166. // 0 and m - 1 is generated.  This is put into the RANDOM-SEED slot of
  167. // the distribution and then divided by m to produce a real between 0
  168. // and 1.
  169. //
  170.  
  171. define method floor/ (x :: <number>, y :: <number>)
  172.     => (q :: <integer>, r :: <number>);
  173.   let res = as(<integer>, (x / y));
  174.   values(res, x - res * y);
  175. end;
  176.  
  177. define method modulo (x :: <number>, y :: <number>) => rem :: <number>;
  178.   let (quo, rem) = floor/(x, y);
  179.   rem;
  180. end;
  181.  
  182. define constant $a$ = 29413621;
  183. define constant $m$ = ash (1, 28);
  184. define constant $k$ = ash (1, 14);
  185. define constant a1 = floor/ ($a$, $k$);
  186. define constant a0 = modulo ($a$, $k$);
  187.  
  188. define method random (dist :: <unit-uniform-distribution>)
  189.       => random :: <double-float>;
  190.    let z = dist.random-seed;
  191.    let z1 = floor/ (z, $k$);
  192.    let z0 = modulo (z, $k$);
  193.    let r = modulo (modulo (a1 * z0 + a0 * z1, $k$) * $k$ + a0 * z0 , $m$);
  194.    dist.random-seed := r;
  195.    as (<double-float>, r) / as (<double-float>, $m$)
  196. end method;
  197.  
  198.  
  199. /* Other Uniform Distributions
  200.  
  201.    Most other random distribution generators rely on the unit uniform
  202.    generator.
  203.  
  204.    A uniform distribution of reals over an arbitrary interval [a, b)
  205.    can be obtained from the unit uniform random variable U by
  206.  
  207.               R = (b - a) U + a
  208.  
  209.    Similarly, a uniform distribution of integers over an arbitrary
  210.    interval [a, b] can be obtained from the unit uniform random
  211.    variable U by
  212.  
  213.               I = round ((b - a) U + a)
  214.  
  215. */
  216.  
  217. // <real-uniform-distribution> -- public
  218. //
  219. // The concrete class for real uniform distributions.  Slots for the
  220. // beginning and ending point of the distribution interval.
  221. //
  222. define class <real-uniform-distribution> (<uniform-distribution>)
  223.    slot unit-uniform :: <unit-uniform-distribution>;
  224.    slot random-from :: <real>,
  225.       required-init-keyword: from:;
  226.    slot random-to :: <real>,
  227.       required-init-keyword: to:;
  228. end class;
  229.  
  230.  
  231. // initialize -- interface
  232. //
  233. // The unit uniform distribution used in the uniform has to be set up.
  234. //
  235. define method initialize (dist :: <real-uniform-distribution>, #key seed (*dylan-random-seed*))
  236.     next-method();
  237.     dist.unit-uniform := make (<unit-uniform-distribution>, seed: seed);
  238. end method;
  239.  
  240.  
  241. // random -- public
  242. //
  243. // Generates real numbers which are distributed uniformly in some
  244. // interval.  Uses the unit uniform generator, and applies the linear
  245. // transformation described above.
  246. //
  247. define method random (dist :: <real-uniform-distribution>)
  248.       => random :: <double-float>;
  249.    (dist.random-to - dist.random-from) * random (dist.unit-uniform) + dist.random-from;
  250. end method;
  251.  
  252.  
  253. // <integer-uniform-distribution> -- public
  254. //
  255. // The concrete class for integer uniform distributions.  Slots for
  256. // the beginning and ending points of the distribution interval.
  257. //
  258. define class <integer-uniform-distribution> (<uniform-distribution>)
  259.    slot unit-uniform :: <unit-uniform-distribution>;
  260.    slot random-from :: <integer>,
  261.       required-init-keyword: from:;
  262.    slot random-to :: <integer>,
  263.       required-init-keyword: to:;
  264. end class;
  265.  
  266.  
  267. // initialize -- interface
  268. //
  269. // The unit uniform distribution used in the uniform has to be set up.
  270. //
  271. define method initialize (dist :: <integer-uniform-distribution>, #key seed (*dylan-random-seed*))
  272.     next-method();
  273.     dist.unit-uniform := make (<unit-uniform-distribution>, seed: seed);
  274. end method;
  275.  
  276.  
  277. // random -- public
  278. //
  279. // Generates integers which are distributed uniformly in some
  280. // interval.  Uses the unit uniform generator, and applies the linear
  281. // transformation described above.
  282. //
  283. // Note: The endpoints of the interval are both inclusive because of
  284. // the rounding.  That is, the numbers generate are in [a, b] instead
  285. // of [a, b).
  286. //
  287.  
  288. define method round (val :: <double-float>) => rval :: <integer>;
  289.     let rval = as(<integer>, val);
  290.     if (val - rval >= 0.5)
  291.         as(<integer>, val + 1.0);
  292.     else
  293.         rval;
  294.     end if;
  295. end method;
  296.  
  297. define method random (dist :: <integer-uniform-distribution>)
  298.       => random :: <integer>;
  299.    round ((dist.random-to - dist.random-from) * random (dist.unit-uniform)
  300.          + dist.random-from)
  301. end method;
  302.  
  303.  
  304. /* Exponential Distribution
  305.  
  306.    The exponential distribution has a cumulative distribution function
  307.    described by
  308.  
  309.                                    -lx
  310.                F(x) = 1 - e      x > 0
  311.  
  312.    An exponential distribution X can be generated from a unit uniform
  313.    distribution U using a transformation.  That is, a number u from
  314.    the uniform is chosen, then the inverse of the CDF is applied
  315.  
  316.                                     1
  317.                X(u) = - - ln(u)
  318.                                     l
  319.  
  320. */
  321.  
  322. // <exponential-distribution> -- public
  323. //
  324. // The concrete class for exponential distributions.  Slot for the
  325. // lambda parameter of the distribution.
  326. //
  327. define class <exponential-distribution> (<random-distribution>)
  328.    slot unit-uniform :: <unit-uniform-distribution>;
  329.    slot lambda :: <real>,
  330.       init-value: 1,
  331.       init-keyword: lambda:;
  332. end class;
  333.  
  334.  
  335. // initialize -- interface
  336. //
  337. // The unit uniform distribution used in the exponential has to be set
  338. // up.
  339. //
  340. define method initialize (dist :: <exponential-distribution>, #key seed (*dylan-random-seed*))
  341.     next-method();
  342.     dist.unit-uniform := make (<unit-uniform-distribution>, seed: seed);
  343. end method;
  344.  
  345. // random -- public
  346. //
  347. // Generates numbers distributed in an exponential distribution with
  348. // parameter lambda.  Applies the inverse CDF of the exponential
  349. // distribution to a number generated from a unit uniform
  350. // distribution.
  351. //
  352. define method random (dist :: <exponential-distribution>) => random :: <double-float>;
  353.    - (log (random (dist.unit-uniform)) / dist.lambda)
  354. end method;
  355.  
  356.  
  357. /* Normal Distribution
  358.  
  359.    The normal (or Gaussian) probability distribution has a very
  360.    complicated probability density function.  So the methods used to
  361.    generate it are somewhat obscure.  The normal distribution can be
  362.    generated using two uniform distributions, A and B.  Numbers from a
  363.    normal distribution with mean 0 and standard deviation (or sigma) 1
  364.    can be found using this formula:
  365.  
  366.                                   1/2
  367.            X = (- 2 ln(A))    cos(2 pi B).
  368.  
  369.    This distribution can be transformed to a distribution with mean m
  370.    and sigma o by a linear function.
  371.  
  372.                  Y = o X + m
  373.  
  374. */
  375.  
  376. // <normal-distribution> -- public
  377. //
  378. // The concrete class for normal distributions.  Slots for the mean
  379. // and standard deviation (sigma) parameters of the distribution.
  380. //
  381. define class <normal-distribution> (<random-distribution>)
  382.    slot unit-uniform-A :: <unit-uniform-distribution>;
  383.    slot unit-uniform-B :: <unit-uniform-distribution>;
  384.    slot mean :: <real>,
  385.       init-value: 0,
  386.       init-keyword: mean:;
  387.    slot sigma :: <real>,
  388.       init-value: 1,
  389.       init-keyword: sigma:;
  390. end class;
  391.  
  392. // initialize -- interface
  393. //
  394. // Both unit uniform distributions used in the normal have to be set
  395. // up.  The first is seeded from the seed given, and the second is
  396. // seeded from a random number generated by the first.
  397. //
  398. define method initialize (dist :: <normal-distribution>, #key seed (*dylan-random-seed*))
  399.     next-method();
  400.     dist.unit-uniform-A := make (<unit-uniform-distribution>, seed: seed);
  401.     dist.unit-uniform-B := make (<unit-uniform-distribution>, seed: random (dist.unit-uniform-A));
  402. end method;
  403.  
  404.  
  405. // random -- public
  406. // 
  407. // Generates a normal distribution with mean 0 and sigma 1 from two
  408. // numbers chosen from a unit uniform distribution, then applies the
  409. // linear transformation to adjust to the real mean and sigma of the
  410. // desired distribution.
  411. // 
  412. // (The constant $pi$ should be predefined or be defined as 4 *
  413. // atan(1) but we don't have atan.)
  414. //
  415. define constant $pi$ = 3.14159265358975;
  416. //
  417. define method random (dist :: <normal-distribution>) => random :: <double-float>;
  418.    let unit-normal-random = sqrt (-2 * log (random (dist.unit-uniform-A)))
  419.                                * cos (2 * $pi$ * random (dist.unit-uniform-B));
  420.    dist.sigma * unit-normal-random + dist.mean;
  421. end method;
  422.  
  423.  
  424. /* Dylan Global Random Distribution
  425.  
  426.    In addition to the variety of random distributions this library
  427.    provides, we also want to have simpler functions for people to use
  428.    without having to create a distribution.
  429.  
  430.    Global variables hold a default Dylan random seed and distribution.
  431.    The function RANDOM-UNIFORM returns a number in some uniform
  432.    distribution.  The function SEED-RANDOM! allows the user to set the
  433.    global random seed.
  434.  
  435. */
  436.  
  437. // *dylan-random-seed* -- public
  438. // 
  439. // This global variable is used as the default seed for
  440. // <unit-uniform-distribution>s (and thus serves as the default seed
  441. // for most other random distributions.
  442. //
  443. define variable *dylan-random-seed* :: <integer> = 42424242;
  444.  
  445.  
  446. // *dylan-random-distribution* -- public
  447. // 
  448. // This global variable stores the default random distribution that is
  449. // used for the following functions.  This gives the user the
  450. // alternative of not having to create their own distributions.
  451. //
  452. define variable *dylan-random-distribution* = make (<unit-uniform-distribution>, seed: *dylan-random-seed*);
  453.  
  454. // random-uniform -- public
  455. // 
  456. // This function returns a random number from a uniform distribution.
  457. // The bounds of the uniform distribution are given by the keywords
  458. // from: and to:.  The type of the number returned is determined by
  459. // the type of the bounds.  (If the bounds do not have the same type
  460. // an error is signalled.)
  461. // 
  462. // If the bounds are <integer>, the random number is rounded.  If the
  463. // bounds are some other type (such as <single-float>, etc.), the
  464. // random number is coerced to that type.
  465. // 
  466. // This function uses *DYLAN-RANDOM-DISTRIBUTION* to generate the
  467. // random number.
  468. //
  469. define constant random-uniform =
  470.    method (#key from: from-bound (0.0), to: to-bound (1.0))
  471.       if (~ object-class (from-bound) == object-class (to-bound))
  472.      error ("Arguments to random-uniform must have same type.");
  473.       end if;
  474.       let random = (to-bound - from-bound)
  475.      * random (*dylan-random-distribution*)
  476.      + from-bound;
  477.       select (object-class (to-bound))
  478.      <integer> =>
  479.         round (random);
  480.      otherwise =>
  481.         as (object-class (to-bound), random);
  482.       end select;
  483.    end method;
  484.  
  485.  
  486. // seed-random! -- public
  487. //
  488. // This functions seeds the default Dylan distribution.
  489. //
  490. define constant seed-random! =
  491.    method (seed :: <integer>)
  492.       if (seed > 0)
  493.      *dylan-random-seed* := seed;
  494.      *dylan-random-distribution* :=
  495.         make (<unit-uniform-distribution>, seed: *dylan-random-seed*);
  496.       else
  497.      error ("Random seed must be > 0: %d", seed);
  498.       end if;
  499.       *dylan-random-seed*
  500.    end method;
  501.  
  502.  
  503. /* Chi-Square Test for the Random Number Generator
  504.  
  505.    The chi-square function can be used to test the integrity of the
  506.    underlying linear congruential generator.  It takes a
  507.    <integer-uniform-distribution> (which should be a distribution on
  508.    an interval [0, r]) and calculates its chi square value.
  509. */
  510.  
  511. // chi-square -- public
  512. // 
  513. // The chi square value of a integer uniform distribution on [0, r] is
  514. // found by taking the sum of the squares of the difference between
  515. // the frequencies of the elements of the distribution and the mean
  516. // frequency.  The mean frequency is the number of samples divided by
  517. // the number of elements in the interval (N / r).  For this to work,
  518. // N should be at least 10 r.
  519. // 
  520. // This function sets up a frequency array and generates N random
  521. // numbers and fills in their frequencies.  It then calculates the chi
  522. // square value of the distribution.
  523. // 
  524. // The chi square value should be no farther from r than twice the
  525. // square root of r.
  526. //
  527. define method chi-square (dist :: <integer-uniform-distribution>)
  528.    let r = dist.random-to;
  529.    let N = 10 * r;
  530.    let f = as (<double-float>, N) / as (<double-float>, r);
  531.    let freq = make (<vector>, size: r + 1, fill: 0);
  532.    for (i from 0 below N)
  533.       let d = random (dist);
  534.       freq[d] := freq[d] + 1;
  535.    end for;
  536.    for (i from 0 below r,
  537.     sample-sum = 0.0 then sample-sum + expt (freq[i] - f, 2))
  538.    finally
  539.       sample-sum / f;
  540.    end for;
  541. end method;
  542.